package com.liebaut.util;

import com.liebaut.dao.article.*;
import com.liebaut.dao.order.DeliveryCostRepository;
import com.liebaut.dao.order.DeliveryOrderRepository;
import com.liebaut.dao.person.CustomerRepository;
import com.liebaut.dao.person.LanguageRepository;
import com.liebaut.dao.person.SupplierRepository;
import com.liebaut.dao.person.TelecomTypeRepository;
import com.liebaut.dao.person.address.AddressTypeRepository;
import com.liebaut.dao.person.address.CountryRepository;
import com.liebaut.dao.reports.ReportRepository;
import com.liebaut.dao.util.UniqueNumberRepository;
import com.liebaut.model.articles.*;
import com.liebaut.model.order.DeliveryCost;
import com.liebaut.model.order.DeliveryOrder;
import com.liebaut.model.order.article.ArticleDetails;
import com.liebaut.model.order.article.Box;
import com.liebaut.model.person.Customer;
import com.liebaut.model.person.Language;
import com.liebaut.model.person.Supplier;
import com.liebaut.model.person.TelecomType;
import com.liebaut.model.person.address.Address;
import com.liebaut.model.person.address.AddressType;
import com.liebaut.model.person.address.Country;
import com.liebaut.model.reports.Report;
import com.liebaut.model.reports.ReportDetails;
import com.liebaut.model.util.UniqueNumber;
import com.liebaut.services.IDGeneratorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.util.*;

public class DBSetup {
    private static final Logger log = LoggerFactory.getLogger(DBSetup.class);

    @Autowired
    AddressTypeRepository addressTypeRepository;

    @Autowired
    TelecomTypeRepository telecomTypeRepository;

    @Autowired
    CountryRepository countryRepository;

    @Autowired
    LanguageRepository languageRepository;

    @Autowired
    PriceClassRepository priceClassRepository;

    @Autowired
    ArticleRepository articleRepository;

    @Autowired
    SizeRepository sizeRepository;

    @Autowired
    ModelRepository modelRepository;

    @Autowired
    SupplierRepository supplierRepository;

    @PersistenceContext
    EntityManager entityManager;

    @Autowired
    CustomerRepository customerRepository;

    @Autowired
    DeliveryOrderRepository deliveryOrderRepository;

    @Autowired
    UniqueNumberRepository uniqueNumberRepository;

    @Autowired
    DeliveryCostRepository deliveryCostRepository;

    @Autowired
    ReportRepository reportRepository;

    @Autowired
    EanCodeRepository eanCodeRepository;

   /* @Autowired
    IDGeneratorService idGeneratorService; */

    private PriceClass kleinhandel;
    private PriceClass groothandel;
    private Country belgium;
    private Language dutch;
    private Supplier espa;

    public void clearSession() {
        entityManager.flush();
        entityManager.clear();
    }

    @Transactional
    public void populateData() throws Exception {
        belgium = countryRepository.save(createCountry("Belgium", "BE"));
        countryRepository.save(createCountry("Germany", "DE"));

        telecomTypeRepository.save(createTelecomType("Email"));
        telecomTypeRepository.save(createTelecomType("Website"));
        telecomTypeRepository.save(createTelecomType("Telefoon"));
        telecomTypeRepository.save(createTelecomType("GSM"));
        telecomTypeRepository.save(createTelecomType("Fax"));

        AddressType verzending = createAddressType("Verzending");
        AddressType facturatie = createAddressType("Facturatie");

        addressTypeRepository.save(verzending);
        addressTypeRepository.save(facturatie);

        dutch = languageRepository.save(createLanguage("Nederlands", "NL"));
        languageRepository.save(createLanguage("English", "EN"));

        kleinhandel = priceClassRepository.save(createPriceClass("Kleinhandel", "Prijzen voor in de winkel."));
        groothandel = priceClassRepository.save(createPriceClass("Groothandel", "Prijzen voor in het magazijn."));

        espa = supplierRepository.save(createSupplier("Espa", dutch, 80));
        for(int i=1; i <= 100; i++){
            if(i != 80){
                supplierRepository.save(createSupplier("Fabrikant " +i, dutch, i));
            }
        }

        Address address1 = createAddress(facturatie, belgium, "Aalst",  "Brusselsesteenweg", "242", "9300");
        Address address2 = createAddress(facturatie, belgium, "Aalst",  "Parklaan", "32", "9300");

        Customer customer = createCustomer("Groothandel Klantennaam", dutch, "100001", "BE 123 456 789", groothandel, 10, address1, "Groothandel Winkel");

        customerRepository.save(customer);
        customerRepository.save(createCustomer("Kleinhandel Klantennaam", dutch, "200001", "BE 987 654 321", kleinhandel, 10, address2, null));

        for(int i=0;i<100;i++){
            customerRepository.save(createCustomer("Klant " + (i+1), dutch, 30000 + (i+1) + "", "BE " + 30000 + (i+1) + "", kleinhandel, 10, null, null));
        }

        UniqueNumber deliveryOrderNumber = createUniqueNumber("deliveryorder", "BB", 2014000001L);
        uniqueNumberRepository.save(deliveryOrderNumber);
        UniqueNumber invoiceNumber = createUniqueNumber("invoice", "FA", 2014000001L);
        uniqueNumberRepository.save(invoiceNumber);

        importArticles();

        DeliveryCost deliveryCost = createDeliveryCost("Klein - nationaal", "Kleine doos, verzonden binnen België", 5.00);
        deliveryCostRepository.save(deliveryCost);

        /*for(int i=0; i < 10; i++){
            deliveryOrderRepository.save(createOrder(10, customer, "BB201400000"+i, new Date(), address1, deliveryCost ));
        }*/
        reportRepository.save(createReport("Bestelbon", "Eerste Bestelbon", "deliveryorder", dutch, "C:\\Users\\Dirk\\Downloads\\report2.jrxml"));
        reportRepository.save(createReport("Factuur", "Eerste Factuur", "invoice", dutch, "C:\\Users\\Dirk\\Downloads\\report2.jrxml"));
    }

    private DeliveryCost createDeliveryCost(String name, String description, double price) {
        DeliveryCost deliveryCost = new DeliveryCost();
        deliveryCost.setDescription(description);
        deliveryCost.setName(name);
        deliveryCost.setPrice(price);
        return deliveryCost;
    }


    public Country createCountry(String name, String countryCode){
        Country country = new Country();
        country.setCountryCode(countryCode);
        country.setName(name);
        return country;
    }

    public TelecomType createTelecomType(String name){
        TelecomType telecomType = new TelecomType();
        telecomType.setName(name);
        return telecomType;
    }

    public AddressType createAddressType(String name){
        AddressType addressType = new AddressType();
        addressType.setName(name);
        return addressType;
    }

    public Language createLanguage(String name, String languageCode){
        Language language = new Language();
        language.setName(name);
        language.setLanguageCode(languageCode);
        return language;
    }

    public PriceClass createPriceClass(String name, String description){
        PriceClass priceClass = new PriceClass();
        priceClass.setName(name);
        priceClass.setDescription(description);
        return priceClass;
    }

    public void importArticles(){
        long start = System.currentTimeMillis();
        String csvFile = "artikelsNiewProg.csv";
        String dir =  "C:\\Users\\Dirk\\Liebaut\\Files\\";
        String[] sizeFiles = {"78-maten.csv"};
        String[] modelFiles = {"78-model.csv"};
        BufferedReader br = null;
        String line = "";
        String cvsSplitBy = ";";
        Article article = null;
        int count = 0;

        try {
            Supplier supplier = null;
            br = new BufferedReader(new FileReader(dir+csvFile));
            while ((line = br.readLine()) != null) {
                String[] array = line.split(cvsSplitBy);
                article = new Article();
                article.setArticleNumber(array[1]);
                article.setDescription(array[2]);
                int supplierNumber = Integer.parseInt(array[0]);
                if(supplier == null || supplier.getNumber() != supplierNumber)
                    supplier= supplierRepository.findByNumber(supplierNumber);
                article.setSupplier(supplier);
                articleRepository.save(article);
                count++;
                //log.info("Number of articles saved: "+count);

            }
            Size size = null;
            log.info("Number of articles saved: "+count);
            long stopArticles = System.currentTimeMillis();
            Random rand = new Random();
            for(String file : sizeFiles){
                if(br != null)
                    br.close();
                br = new BufferedReader(new FileReader(dir+file));
                article = null;
                count = 0;
                int countSizes = 0;
                while ((line = br.readLine()) != null) {
                    String[] array = line.split(cvsSplitBy);
                    size = new Size();
                    size.setName(array[2]);
                    String articleNumber = array[1];
                    int supplierNumber = Integer.parseInt(array[0]);
                    if(article == null || !article.getArticleNumber().equalsIgnoreCase(articleNumber) || article.getSupplier().getNumber() != supplierNumber){
                        if(article != null){
                            PurchaseDetails purchaseDetails = new PurchaseDetails();
                            purchaseDetails.setPrice(rand.nextDouble() * 20);
                            Set<PurchaseDetails> purchaseDetailsSet = new HashSet<PurchaseDetails>();
                            purchaseDetailsSet.add(purchaseDetails);
                            article.setPurchaseDetails(purchaseDetailsSet);
                            Price price1 = new Price();
                            price1.setFactor(1.45);
                            price1.setPrice(Math.ceil(purchaseDetails.getPrice() * 1.45 * 5) / 5);
                            price1.setPriceClass(groothandel);
                            Price price2 = new Price();
                            price2.setFactor(2.45);
                            price2.setPrice(Math.ceil(purchaseDetails.getPrice() * 2.45*5)/5);
                            price2.setPriceClass(kleinhandel);
                            Set<Price> prices = new HashSet<Price>();
                            prices.add(price1);
                            prices.add(price2);
                            article.setPrices(prices);
                            articleRepository.save(article);
                            count++;
                            //log.info("Number of articles sizes saved: "+count);
                        }
                        List<Article> list = articleRepository.findByArticleNumberAndSupplier(array[1], Integer.parseInt(array[0]));
                        if(list != null && list.size() == 1){
                            article = list.get(0);
                        } else {
                            article = null;
                            if(list.size() == 0)
                                log.error("Article not found: " + array[0] + "/"+array[1]);
                            else
                                log.error("Multiple articles found: " + array[0] + "/"+array[1]);
                        }
                    } if(article != null){
                        Set<Size> sizes = article.getSizes();
                        if(sizes == null)
                            sizes = new HashSet<Size>();
                        if(size.getName() != null){
                            sizes.add(sizeRepository.save(size));
                            article.setSizes(sizes);
                            countSizes ++;
                        }
                    }
                }
                log.info("Number of sizes saved: "+countSizes);
            }
            Model model = null;
            for(String file : modelFiles){
                if(br != null)
                    br.close();
                br = new BufferedReader(new FileReader(dir+file));
                article = null;
                count = 0;
                int countModels = 0;
                while ((line = br.readLine()) != null) {
                    String[] array = line.split(cvsSplitBy);
                    model = new Model();
                    model.setCode(array[2]);
                    model.setName(array[3]);
                    String articleNumber = array[1];
                    int supplierNumber = Integer.parseInt(array[0]);
                    if(article == null || !article.getArticleNumber().equalsIgnoreCase(articleNumber) || article.getSupplier().getNumber() != supplierNumber){
                        if(article != null){
                            articleRepository.save(article);
                            count++;
                        }
                        List<Article> list = articleRepository.findByArticleNumberAndSupplier(array[1], Integer.parseInt(array[0]));
                        if(list != null && list.size() == 1){
                            article = list.get(0);
                        } else {
                            article = null;
                            if(list.size() == 0)
                                log.error("Article not found: " + array[0] + "/"+array[1]);
                            else
                                log.error("Multiple articles found: " + array[0] + "/"+array[1]);
                        }
                    } if(article != null){
                        Set<Model> models = article.getModels();
                        if(models == null)
                            models = new HashSet<Model>();
                        if(model.getName() != null){
                            models.add(model);
                            article.setModels(models);
                            countModels ++;
                        }
                    }
                }
                log.info("Number of models saved: "+countModels);
            }
            count = 0;
            for(Article a : articleRepository.findAll()){
                if(a.getSizes() != null){
                    for(Size s: a.getSizes()){
                        if(a.getModels() != null){
                            for(Model m:a.getModels()){
                                EanCode ean = new EanCode();
                                ean.setEanCode(count);
                                ean.setModel(m);
                                ean.setSize(s);
                                addEanCode(a, ean);
                                count++;
                            }
                        } else{
                            EanCode ean = new EanCode();
                            ean.setEanCode(count);
                            ean.setSize(s);
                            addEanCode(a, ean);
                            count++;
                        }
                    }
                } else {
                    EanCode ean = new EanCode();
                    ean.setEanCode(count);
                    addEanCode(a, ean);
                    count++;
                }
                articleRepository.save(article);
            }
            log.info("Number of articles sizes saved: "+count);

            long stop = System.currentTimeMillis();
            log.info("Importing articles took " + (stop-start) + " ms.");
            log.info("Importing articles only took " + (stopArticles-start) + " ms.");
            log.info("Importing sizes took " + (stop-stopArticles) + " ms.");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void addSize(Article article, String sizeName){
        Size size = new Size();
        size.setName(sizeName);
        Set<Size> sizes = article.getSizes();
        if(sizes == null){
            sizes = new HashSet<Size>();
            article.setSizes(sizes);
        }
        sizes.add(sizeRepository.save(size));
    }

    private void addModel(Article article, String modelCode, String modelName){
        if(modelCode != null && !modelCode.equals("")){
            Model model = new Model();
            model.setName(modelName);
            model.setCode(modelCode);
            Set<Model> models = article.getModels();
            if(models == null){
                models = new HashSet<Model>();
                article.setModels(models);
            }
            models.add(modelRepository.save(model));
        }
    }

    private void addPurchasePrice(Article article, double price, double discount){
        PurchaseDetails purchase = new PurchaseDetails();
        purchase.setPrice(price);
        purchase.setDiscount(discount);
        Date now = new Date();
        purchase.setDatecreated(now);
        purchase.setLastUpdated(now);
        Set<PurchaseDetails> purchases = article.getPurchaseDetails();
        if(purchases == null){
            purchases = new HashSet<PurchaseDetails>();
            article.setPurchaseDetails(purchases);
        }
        purchases.add(purchase);
    }

    private void addEanCode(Article article, EanCode eanCode){
        Set<EanCode> eanCodes = article.getEanCodes();
        if(eanCodes == null)
            eanCodes = new HashSet<EanCode>();
        eanCodes.add(eanCode);
        article.setEanCodes(eanCodes);
    }

    private void addPrice(Article article, double price, PriceClass priceClass, double factor){
        Price p = new Price();
        p.setPrice(price);
        p.setPriceClass(priceClass);
        p.setFactor(factor);
        Date now = new Date();
        p.setDateCreated(now);
        p.setLastUpdated(now);
        Set<Price> prices = article.getPrices();
        if(prices == null){
            prices = new HashSet<Price>();
            article.setPrices(prices);
        }
        prices.add(p);
    }

    private Supplier createSupplier(String name, Language language, int number){
        Supplier supplier = new Supplier();
        supplier.setName(name);
        supplier.setLanguage(language);
        supplier.setNumber(number);
        return supplier;
    }

    private Customer createCustomer(String name, Language language, String number, String vatNumber, PriceClass priceClass, int discount, Address address, String extraName){
        Customer customer = new Customer();
        customer.setName(name);
        customer.setDiscount(discount);
        customer.setLanguage(language);
        customer.setNumber(number);
        customer.setPriceClass(priceClass);
        customer.setVatNumber(vatNumber);
        if(address != null){
            Set<Address> addresses = new HashSet<Address>();
            addresses.add(address);
            customer.setAddresses(addresses);
        }
        customer.setExtraName(extraName);
        return customer;
    }

    private Address createAddress(AddressType type, Country country, String location, String street, String number, String postalCode){
        Address address = new Address();
        address.setAddressType(type);
        address.setCountry(country);
        address.setDefaultAddress(true);
        address.setLocation(location);
        address.setNumber(number);
        address.setStreet(street);
        address.setPostalCode(postalCode);
        return  address;
    }

    private DeliveryOrder createOrder(double discount, Customer customer, String number, Date date, Address address, DeliveryCost deliveryCost){
        DeliveryOrder order = new DeliveryOrder();
        order.setDiscount(discount);
        order.setCustomer(customer);
        order.setDeliveryNumber(number);
        order.setDueDate(date);
        order.setAddress(address);
        Set<Box> boxes = new HashSet<Box>();
        int[] discounts = {0,5,10,15};
        for(int i=1; i<=2; i++){
            Box box = new Box();
            box.setDescription("Doos");
            box.setNumber(i);
            Set<ArticleDetails> articles = new HashSet<ArticleDetails>();
            Random random = new Random();
            for(int j=i*5; j<i*5+5;j++){
                Article article = articleRepository.findOne(new Long(j));
                ArticleDetails articleDetails = new ArticleDetails();
                articleDetails.setDescription(article.getDescription());
                articleDetails.setAmount(random.nextInt(20));
                articleDetails.setArticle(article);
                articleDetails.setDiscount(discounts[random.nextInt(4)]);
                for(Price price : article.getPrices()){
                    articleDetails.setPrice(price.getPrice());
                    continue;
                }
                for(Size size : article.getSizes()){
                    articleDetails.setSize(size.getName());
                    continue;
                }

                articles.add(articleDetails);
            }
            box.setArticles(articles);
            boxes.add(box);
        }
        order.setBoxes(boxes);
        order.setDeliveryCost(deliveryCost);
        return order;
    }

    private UniqueNumber createUniqueNumber(String description, String prefix, long startNumber){
        UniqueNumber number = new UniqueNumber();
        number.setDescription(description);
        number.setPrefix(prefix);
        number.setNumber(startNumber);
        return number;
    }

    private Report createReport(String name, String description, String type, Language language, String location){
        Report report = new Report();
        ReportDetails reportDetails = new ReportDetails();
        reportDetails.setFilePath(location);
        reportDetails.setLanguage(language);
        Set<ReportDetails> details = new HashSet<ReportDetails>();
        details.add(reportDetails);
        report.setReportDetails(details);
        report.setDescription(description);
        report.setName(name);
        report.setType(type);
        return report;
    }


}
